import { ESCesiumViewer } from "@czmSrc/ESCesiumViewer";
import { Editing } from "./Editing";
import { Destroyable, Event, extendClassProps, getEventFromPromise, ObjResettingWithEvent, react, reactArrayWithUndefined, ReactivePropsToNativePropsAndChanged } from "xbsj-base";
import { CzmPoint } from "@czmSrc/CzmObjects";
import { pickPosition } from "@czmSrc/utils";
import { ESJVector3D } from "earthsdk3";

export type GeoCoordinatesPickerPickingFuncType = ((pointerEvent: PointerEvent) => boolean);

export class PlaceEditing extends Editing {
    static defaultConfig = {
        clickEnabled: true,
        dblClickEnabled: false
    } as {
        clickEnabled?: boolean,
        dblClickEnabled?: boolean
    };
    // 注意这里用的是react，不是reactJson，意思就是一个配置信息可以配置多个不同的PositionEditing
    private _config = this.disposeVar(react(PlaceEditing.defaultConfig));
    get config() { return this._config.value; }
    set config(value) { this._config.value = value; }
    get configChanged() { return this._config.changed; }
    // 放置完成
    private _overEvent = this.disposeVar(new Event());
    get overEvent() { return this._overEvent; }
    over() { this._overEvent.emit(); }

    static defaultPointerEventFilterFunc = (pointerEvent: PointerEvent) => {
        return pointerEvent.button === 0;
    }

    static override defaults = {
        ...Editing.defaults,
        position: [116.39, 39.9, 0] as [number, number, number], // 经度纬度高度，度为单位
    };

    private _pointerEventFilterFunc = this.disposeVar(react<GeoCoordinatesPickerPickingFuncType>(PlaceEditing.defaultPointerEventFilterFunc));
    get pointerEventFilterFunc() { return this._pointerEventFilterFunc.value; }
    set pointerEventFilterFunc(value: GeoCoordinatesPickerPickingFuncType) { this._pointerEventFilterFunc.value = value; }
    get pointerEventFilterFuncChanged() { return this._pointerEventFilterFunc.changed; }

    constructor(czmViewer: ESCesiumViewer) {
        super();
        if (!czmViewer.viewer) return;
        {
            const update = () => {
                const { config } = this;
                (config.clickEnabled !== undefined) && (this.clickEnabled = config.clickEnabled);
                (config.dblClickEnabled !== undefined) && (this.dblClickEnabled = config.dblClickEnabled);
            };
            update();
            this.dispose(this.configChanged.disposableOn(update));
        }
        //创建坐标示意点
        {
            const point = this.ad(new CzmPoint(czmViewer));
            point.pixelSize = 4;
            point.outlineColor = [0, 0, 0.8, 1];
            point.outlineWidth = 2;
            point.allowPicking = false;
            {
                const update = () => {
                    if (!this.position) return;
                    const tempPos = [...this.position] as [number, number, number];
                    czmViewer.editingHeightOffset && (tempPos[2] -= czmViewer.editingHeightOffset);
                    point.position = tempPos;
                    point.show = !!this.position;
                }
                update();
                this.dispose(this.positionChanged.disposableOn(update));
            }
        }
        this.ad(new ObjResettingWithEvent(this.enabledChanged, () => {
            return this.enabled ? new PickingProcessing(czmViewer, this) : undefined
        }))
    }
}
export namespace PlaceEditing {
    export const createDefaultProps = () => ({
        ...Editing.createDefaultProps(),
        position: reactArrayWithUndefined<ESJVector3D>(undefined),
        virtualHeight: undefined as number | undefined,
        clickEnabled: true,
        dblClickEnabled: false,
    });
}
extendClassProps(PlaceEditing.prototype, PlaceEditing.createDefaultProps);
export interface PlaceEditing extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof PlaceEditing.createDefaultProps>> { }

class PickingProcessing extends Destroyable {
    get czmViewer() { return this._czmViewer; }
    get placeEditing() { return this._placeEditing; }

    private _doings: PickingPositionDo[] = [];
    private initDoings = this.ad(() => {
        this._doings.forEach(doing => doing.destroy());
        this._doings.length = 0;
    })

    constructor(private _czmViewer: ESCesiumViewer, private _placeEditing: PlaceEditing) {
        super();
        {
            // 结束拾取
            const overPlaceEditing = (pointerEvent: PointerEvent, pointerMode: 'click' | 'dblclick') => {
                if (pointerMode == 'click' && (!_placeEditing.clickEnabled || !_placeEditing.pointerEventFilterFunc(pointerEvent))) return;
                if (pointerMode == 'dblclick' && (!_placeEditing.dblClickEnabled || !_placeEditing.pointerEventFilterFunc(pointerEvent))) return;
                _placeEditing.over();
                _placeEditing.enabled = false;
            }
            this.ad(_czmViewer.clickEvent.don(pointerEvent => {
                pointerEvent.pointerEvent && overPlaceEditing(pointerEvent.pointerEvent, 'click');
            }))
            this.ad(_czmViewer.dblclickEvent.don(pointerEvent => {
                pointerEvent.pointerEvent && overPlaceEditing(pointerEvent.pointerEvent, 'dblclick');
            }))
        }
        {
            //鼠标移动获取位置
            this.ad(_czmViewer.pointerMoveEvent.don(pointerEvent => {
                if (!pointerEvent.pointerEvent) return;
                const doing = new PickingPositionDo(this, pointerEvent.pointerEvent, position => {
                    // 有可能取到地下，所以需要判断一下
                    if (!position || position[2] < -100000) {
                        _placeEditing.position = undefined;
                        return;
                    };
                    const tempPos = [...position] as [number, number, number];
                    _czmViewer.editingHeightOffset && (tempPos[2] += _czmViewer.editingHeightOffset);
                    _placeEditing.position = tempPos;
                    // 执行完毕后，从数组中移除
                    const deletedItems = this._doings.splice(0, this._doings.indexOf(doing) + 1);
                    deletedItems.forEach(item => item.destroy());
                });
                this._doings.push(doing);
            }))
        }
    }
}
class PickingPositionDo extends Destroyable {
    constructor(private _owner: PickingProcessing, pointerEvent: PointerEvent, resultFunc: (position: [number, number, number] | undefined) => void) {
        super();
        const { czmViewer } = this._owner;
        const promise = pickPosition(czmViewer, pointerEvent, this._owner.placeEditing.virtualHeight);
        const [event] = getEventFromPromise(promise);
        this.dispose(event.disposableOn(resultFunc));
    }
}